package gulimall.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import gulimall.constant.ProductConstant;
import gulimall.entity.*;
import gulimall.feign.CouponFeignService;
import gulimall.feign.ElasticSearchFeignService;
import gulimall.feign.WareFeignService;
import gulimall.mapper.BrandMapper;
import gulimall.mapper.CategoryMapper;
import gulimall.mapper.SpuInfoDescMapper;
import gulimall.mapper.SpuInfoMapper;
import gulimall.service.*;
import gulimall.to.HasStockVo;
import gulimall.to.SkuReductionTo;
import gulimall.to.SpuBoundTo;
import gulimall.to.es.SkuEsModel;
import gulimall.utils.PageUtils;
import gulimall.utils.Query;
import gulimall.utils.R;
import gulimall.vo.SpuInfoRespVo;
import gulimall.vo.spusave.Attr;
import gulimall.vo.spusave.BaseAttrs;
import gulimall.vo.spusave.Skus;
import gulimall.vo.spusave.SpuSaveVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;


@Slf4j
@Service("spuInfoService")
public class SpuInfoServiceImpl extends ServiceImpl<SpuInfoMapper, SpuInfo> implements SpuInfoService {

    @Resource
    private SpuInfoDescMapper spuInfoDescMapper;
    @Resource
    private SpuImagesService spuImagesService;
    @Resource
    private ProductAttrValueService productAttrValueService;
    @Resource
    private SkuImagesService skuImagesService;
    @Resource
    private SkuInfoService skuInfoService;
    @Resource
    private AttrService attrService;
    @Resource
    private SkuSaleAttrValueService skuSaleAttrValueService;
    @Resource
    private CouponFeignService couponFeignService;

    @Resource
    private WareFeignService wareFeignService;

    @Resource
    private ElasticSearchFeignService elasticSearchFeignService;

    @Resource
    private CategoryMapper categoryMapper;
    @Resource
    private BrandMapper brandMapper;

    /**
     * spu检索
     *
     * @param params
     * @return
     */
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<SpuInfo> queryWrapper = new QueryWrapper<>();
        String key = (String) params.get("key");
        String catelogId = (String) params.get("catelogId");
        String brandId = (String) params.get("brandId");
        String status = (String) params.get("status");
        if (StringUtils.isNotBlank(catelogId) && !catelogId.equalsIgnoreCase("0")) {
            queryWrapper.lambda().eq(SpuInfo::getCatalogId, Long.valueOf(catelogId));
        }
        if (StringUtils.isNotBlank(brandId) && !brandId.equalsIgnoreCase("0")) {
            queryWrapper.lambda().eq(SpuInfo::getBrandId, Long.valueOf(brandId));
        }

        if (StringUtils.isNotBlank(status)) {
            queryWrapper.lambda().eq(SpuInfo::getPublishStatus, Integer.valueOf(status));
        }
        if (StringUtils.isNotBlank(key)) {
            queryWrapper.and(wrapper -> {
                wrapper.lambda()
                        .eq(SpuInfo::getId, key)
                        .or().like(SpuInfo::getSpuName, key);
            });
        }

        List<SpuInfo> spuInfos = baseMapper.selectList(queryWrapper);
        List<SpuInfoRespVo> spuInfoRespVos = spuInfos.stream().map(spuInfo -> {
            SpuInfoRespVo spuInfoRespVo = new SpuInfoRespVo();
            BeanUtils.copyProperties(spuInfo, spuInfoRespVo);
            //设置分类名称
            if (StringUtils.isNotBlank(catelogId) && !catelogId.equalsIgnoreCase("0")) {
                String catelogName = categoryMapper.selectById(Long.valueOf(catelogId)).getName();
                spuInfoRespVo.setCatelogName(catelogName);
            }
            //设置品牌名称
            if (StringUtils.isNotBlank(brandId) && !brandId.equalsIgnoreCase("0")) {
                String brandName = brandMapper.selectById(Long.valueOf(brandId)).getName();
                spuInfoRespVo.setBrandName(brandName);
            }
            return spuInfoRespVo;
        }).collect(Collectors.toList());

        IPage<SpuInfo> page = this.page(
                new Query<SpuInfo>().getPage(params),
                new QueryWrapper<SpuInfo>()
        );

        PageUtils pageUtils = new PageUtils(page);
        pageUtils.setList(spuInfoRespVos);

        return pageUtils;
    }

    /**
     * 保存商品信息，新增商品
     *
     * @param spuSaveVo
     */
    @Override
    @Transactional
    public void saveSpuInfo(SpuSaveVo spuSaveVo) {
        //一、保存spu相关信息
        //1.保存spu基本信息(pms_spu_info)
        SpuInfo spuInfo = new SpuInfo();
        BeanUtils.copyProperties(spuSaveVo, spuInfo);
        baseMapper.insert(spuInfo);

        //2.保存spu商品介绍图片(pms_spu_info_desc)
        List<String> decript = spuSaveVo.getDecript();
        if (!decript.isEmpty()) {
            //将decript集合中的元素转换为字符串并用","连接
            String stringDecript = String.join(",", decript);
            SpuInfoDesc spuInfoDesc = new SpuInfoDesc();
            spuInfoDesc.setSpuId(spuInfo.getId());
            spuInfoDesc.setDecript(stringDecript);
            spuInfoDescMapper.insert(spuInfoDesc);
        }

        //3.保存spu图片集(pms_spu_images)
        List<String> images = spuSaveVo.getImages();
        if (!images.isEmpty()) {
            spuImagesService.saveImages(spuInfo.getId(), images);
        }

        //5.保存spu规格参数(pms_product_attr_value)
        List<BaseAttrs> baseAttrs = spuSaveVo.getBaseAttrs();
        if (!baseAttrs.isEmpty()) {
            productAttrValueService.saveAttrs(spuInfo.getId(), baseAttrs);
        }

        //6.保存spu的积分信息(成长值，购物积分<金币>)(sms_spu_bounds)(远程服务调用)
        SpuBoundTo spuBoundTo = new SpuBoundTo();
        BeanUtils.copyProperties(spuSaveVo.getBounds(), spuBoundTo);
        spuBoundTo.setSpuId(spuInfo.getId());
        R saveSpuBound = couponFeignService.save(spuBoundTo);
        if (saveSpuBound.getCode() != 0) {
            log.error("调用远程服务gulimall-coupon保存spu积分信息失败！");
        }

        //二、保存当前spu对应的sku相关信息
        List<Skus> skusList = spuSaveVo.getSkus();
        if (!skusList.isEmpty()) {
            skusList.forEach(skus -> {
                //1.保存sku基本信息(pms_sku_info)
                SkuInfo skuInfo = new SkuInfo();
                //1.1保存sku基本信息中的skuDefaultImg
                skus.getImages().forEach(img -> {
                    //如果是默认图片就给sku的skuDefaultImg赋值
                    if (img.getDefaultImg() == 1) {
                        skuInfo.setSkuDefaultImg(img.getImgUrl());
                    }
                });

                //1.2保存sku基本信息中的其他信息
                BeanUtils.copyProperties(skus, skuInfo);//price skuName skuTitle skuSubtitle
                skuInfo.setSpuId(spuInfo.getId());//spuId
                skuInfo.setBrandId(spuInfo.getBrandId());//brandId
                skuInfo.setCatalogId(spuSaveVo.getCatalogId());//catelogId
                skuInfo.setSaleCount(0L);//saleCount销量默认为0
                skuInfoService.save(skuInfo);//skuInfo新增后会回传主键skuId

                //2.保存sku图片信息(pms_sku_images)
                List<SkuImages> imagesList = skus.getImages().stream().map(img -> {
                    SkuImages skuImages = new SkuImages();
                    BeanUtils.copyProperties(img, skuImages);//imgUrl defaultImg
                    skuImages.setSkuId(skuInfo.getSkuId());//获取skuInfo新增后的主键Id
                    return skuImages;
                }).filter(img -> {//筛选出imgUrl不为null的
                    //返回true就会被收集
                    return StringUtils.isNotBlank(img.getImgUrl());
                }).collect(Collectors.toList());
                skuImagesService.saveBatch(imagesList);

                //3.保存sku的销售属性(pms_sku_sale_attr_value)
                List<Attr> attrList = skus.getAttr();
                List<SkuSaleAttrValue> skuSaleAttrValues = attrList.stream().map(attr -> {
                    SkuSaleAttrValue skuSaleAttrValue = new SkuSaleAttrValue();
                    BeanUtils.copyProperties(attr, skuSaleAttrValue);//attrId attrName attrValue
                    skuSaleAttrValue.setSkuId(skuInfo.getSkuId());//skuId
                    skuSaleAttrValue.setAttrSort(0);//排序默认为0
                    return skuSaleAttrValue;
                }).collect(Collectors.toList());
                skuSaleAttrValueService.saveBatch(skuSaleAttrValues);

                //4.保存sku的满减信息(sms_sku_full_reduction)(远程服务调用)
                //5.保存sku的折扣信息(sms_sku_ladder)(远程服务调用)
                //6.保存sku会员价格相关信息(sms_member_price)(远程服务调用)
                SkuReductionTo skuReductionTo = new SkuReductionTo();
                BeanUtils.copyProperties(skus, skuReductionTo);
                skuReductionTo.setMemberPrice(skus.getMemberPrice());
                //设置SkuReductionTo中的skuId
                skuReductionTo.setSkuId(skuInfo.getSkuId());

                R saveSkuReduction = couponFeignService.saveSkuReduction(skuReductionTo);
                if (saveSkuReduction.getCode() != 0) {
                    log.error("调用远程服务gulimall-coupon保存sku优惠信息失败！");
                }
            });
        }

    }

    /**
     * 商品上架
     *
     * @param spuId
     */
    @Override
    @Transactional
    public void up(Long spuId) {
        //1.根据spuId查询出attrId然后在attr表中查询允许被检索的attr
        List<Long> attrIds = productAttrValueService.list(
                new QueryWrapper<ProductAttrValue>().lambda()
                        .eq(ProductAttrValue::getSpuId, spuId)
        ).stream().map(ProductAttrValue::getAttrId).collect(Collectors.toList());

        //查询出spuId对应的所有Attr
        List<gulimall.entity.Attr> attrList = attrService.listByIds(attrIds);
        //过滤出允许被检索的Attr
        attrList = attrList.stream().filter(attr -> attr.getSearchType() == 1).collect(Collectors.toList());
        //获取这些attr的id
        List<Long> attrIds2 = attrList.stream().map(gulimall.entity.Attr::getAttrId).collect(Collectors.toList());

        //查询出spuId对应的能够被检索的product_attr_value(规格参数)
        List<ProductAttrValue> productAttrValues = productAttrValueService.list(
                new QueryWrapper<ProductAttrValue>().lambda()
                        .in(ProductAttrValue::getAttrId, attrIds2)
        );
        List<SkuEsModel.Attrs> attrsList = productAttrValues.stream().map(productAttrValue -> {
            SkuEsModel.Attrs attrs = new SkuEsModel.Attrs();
            BeanUtils.copyProperties(productAttrValue, attrs);
            return attrs;
        }).collect(Collectors.toList());


        //2.根据spuId查询出所有sku的信息，并组装成为SkuEsModel的List集合
        List<SkuInfo> list = skuInfoService.list(
                new QueryWrapper<SkuInfo>().lambda()
                        .eq(SkuInfo::getSpuId, spuId)
        );

        //调用gulimall-ware服务查询是否含有库存
        Map<Long, Boolean> hasStockMap = null;
        try {
            List<Long> skuIds = list.stream().map(SkuInfo::getSkuId).collect(Collectors.toList());
            R r = wareFeignService.hasStockBySkuIds(skuIds);

            /**
             * 由于feign底层会把返回数据的list集合转换为LinkedHashMap
             * 所以需要先序列化然后反序列化，才能获取到List集合
             */
            Object hasStockList = r.get("hasStockList");
            ObjectMapper mapper = new ObjectMapper();
            String stringJson = mapper.writeValueAsString(hasStockList);
            ArrayList<HasStockVo> vos = mapper.readValue(stringJson, new TypeReference<ArrayList<HasStockVo>>() {
            });


            hasStockMap = vos.stream().collect(Collectors.toMap(HasStockVo::getSkuId, HasStockVo::getHasStock));
        } catch (Exception e) {
            log.error("调用远程服务gulimall-ware失败，异常原因: {}", e.getMessage());
            e.printStackTrace();
            throw new RuntimeException(e);
        }


        Map<Long, Boolean> finalHasStockMap = hasStockMap;
        List<SkuEsModel> upProducts = list.stream().map(skuInfo -> {
            SkuEsModel skuEsModel = new SkuEsModel();
            //拷贝属性名相同的属性
            BeanUtils.copyProperties(skuInfo, skuEsModel);

            //设置属性名不同的属性（skuPrice，skuImg，catelogId）
            skuEsModel.setSkuPrice(skuInfo.getPrice());
            skuEsModel.setSkuImg(skuInfo.getSkuDefaultImg());
            skuEsModel.setCatelogId(skuInfo.getCatalogId());

            //设置hasStock，hotScore，catelogName，brandName，brandImg，attrs
            //1.设置hasStock
            if (finalHasStockMap == null) { //如果为null就设置为true
                skuEsModel.setHasStock(true);
            } else {
                Boolean hasStock = finalHasStockMap.get(skuInfo.getSkuId());
                skuEsModel.setHasStock(hasStock != null);
            }

            //2.TODO hotScore 热度评分
            skuEsModel.setHotScore(0L);//默认先设置为0
            //3.catelogName
            String catelogName = categoryMapper.selectById(skuInfo.getCatalogId()).getName();
            skuEsModel.setCatelogName(catelogName);
            //4.brandName brandImg
            Brand brand = brandMapper.selectById(skuInfo.getBrandId());
            skuEsModel.setBrandName(brand.getName());
            skuEsModel.setBrandImg(brand.getLogo());
            //5.attrs
            //设置attrs
            skuEsModel.setAttrs(attrsList);

            return skuEsModel;
        }).collect(Collectors.toList());

        //调用gulimall-search服务，保存数据到ElasticSearch中
        R r = elasticSearchFeignService.productStatusUp(upProducts);
        if (r.getCode() == 0) {
            //改变spu状态为已上架
            baseMapper.updateSpuStatus(spuId, ProductConstant.StatusEnum.SPU_UP.getCode());
        } else {
            //TODO 重复调用？接口幂等性？重试机制
            log.error("远程调用gulimall-search服务失败！");
        }

    }

}